home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / database / hl / hl-1.002 / hl-1 / Lib / Terminal.cc < prev    next >
Encoding:
C/C++ Source or Header  |  1995-04-15  |  25.3 KB  |  1,053 lines

  1. /* -*- Mode: C -*- */
  2. /* Terminal.cc - Terminal class implementation
  3.  *        Inspired by the User Interface Manager code in chapter
  4.  *        12 of "C++, a guide for C programmers",    by Sharam Hekmatpour,
  5.  *        (c) 1990 by Prentice Hall of Australia Pty Ltd.
  6.  * Created by Robert Heller on Sat Dec  7 22:05:49 1991
  7.  *
  8.  * ------------------------------------------------------------------
  9.  * Home Libarian by Deepwoods Software
  10.  * Common Class library implementation code
  11.  * ------------------------------------------------------------------
  12.  * Modification History:
  13.  * ------------------------------------------------------------------
  14.  * Contents:
  15.  * ------------------------------------------------------------------
  16.  * 
  17.  * 
  18.  * Copyright (c) 1991,1992 by Robert heller (D/B/A Deepwoods Software)
  19.  *        All Rights Reserved
  20.  * 
  21.  */
  22. #include <Terminal.h>
  23.  
  24. #ifndef MESSYDOS
  25. // Globals used by termcap library...
  26. // (Weirdness of OSK's termcap library...)
  27. #ifdef OSK
  28. char PC_;
  29. #else
  30. char PC;
  31. #endif
  32. char* BC;
  33. char* UP;
  34. #ifdef unix
  35. unsigned int ospeed;
  36. #else
  37. short ospeed;
  38. #endif
  39. #endif
  40.  
  41. #ifdef unix
  42. #include <stdio.h>
  43. #include <signal.h>
  44. #endif
  45.  
  46. short Terminal::cline;            // current line
  47. short Terminal::ccol;            // current column
  48. short Terminal::lines;            // screen lines
  49. short Terminal::colms;            // screen columns
  50. ErrFun Terminal::errFun;
  51. char   Terminal::termBuf[bufSize];
  52. Terminal* Terminal::term;
  53. #ifdef OSK
  54. sgbuf Terminal::ttym;
  55. sgbuf Terminal::xttym;
  56. #endif
  57. #ifdef unix
  58. struct termios Terminal::ttym;
  59. struct termios Terminal::xttym;
  60. #endif
  61.  
  62. #ifndef MESSYDOS
  63. char Terminal::TermType[48];        // terminal type name
  64. char Terminal::TCapBuf[TCapsLen];        // termcap buffer
  65. char Terminal::tcapbuf[TCapsLen];        // copy
  66.     // termcap capabilities
  67. char* Terminal::BC;            // backspace character
  68. char* Terminal::UP;            // up line sequence
  69. char* Terminal::CL;            // clear screen
  70. char* Terminal::CM;            // cursor movement
  71. char* Terminal::CE;            // clear to eol
  72. char* Terminal::CD;            // clear to eos
  73. char* Terminal::SO;            // standout start
  74. char* Terminal::SE;            // standout end
  75. char* Terminal::HO;            // home cursor
  76. char* Terminal::KD;            // down arrow key
  77. char* Terminal::KU;            // up arrow key
  78. char* Terminal::KR;            // right arrow key
  79. char* Terminal::KL;            // left arrow key
  80. char* Terminal::KH;            // home key
  81. char* Terminal::KB;            // backspace key
  82. char* Terminal::TI;            // terminal init
  83. char* Terminal::TE;            // terminal de-init
  84. short Terminal::ospeed;            // baud rate (used for padding)
  85. char Terminal::PC;                // pad character
  86. #endif
  87.  
  88. // Interrupt handler...
  89. int Interrupt()
  90. {
  91.     Terminal::term->PlainPen();
  92.     Terminal::term->ExitChars();
  93. #ifdef OSK
  94.     _ss_opt(0, &(Terminal::ttym));
  95.     intercept(0);
  96. #endif
  97. #ifdef unix
  98.     tcsetattr(0, TCSANOW, &(Terminal::ttym));
  99.     signal(SIGINT,0);
  100. #endif
  101.     exit(1);
  102.     return(0);
  103. }
  104.  
  105.  
  106. #ifdef OSK
  107. // This is how signals are handled by OSK programs...
  108. static void Intercept(int sig)
  109. {
  110.     if (sig == SIGINT) Interrupt();
  111.     return;
  112. }
  113. #endif
  114.  
  115. // Constructor: fetch terminal type, termcap info, and initialize terminal
  116. Terminal::Terminal ()
  117. {
  118.     char* temp;
  119.  
  120.     // make sure called only once
  121.     if (term != 0) Error(termErr,"in Terminal");
  122.     term = this;        // self reference
  123. #ifdef MESSYDOS
  124.     gppconio_init();
  125.     text_info screen_info;
  126.     gettextinfo(&screen_info);
  127.     lines = screen_info.screenheight;
  128.     colms = screen_info.screenwidth;
  129.     cline = 0;
  130.     ccol  = 0;
  131.     PlainPen();
  132. #else
  133.     SE = "";        // (set here so Error won't crash)
  134. #ifdef OSK
  135.     // Get vanila terminal modes
  136.     if (_gs_opt(0, &ttym) < 0) Error(sysErr,"for _gs_opt");
  137. #endif
  138. #ifdef unix
  139.     // Get vanila terminal modes
  140.     if (tcgetattr(0, &ttym) < 0) Error(sysErr,"for tcgetattr");
  141. #endif
  142.     // Get terminal type
  143.     char *tt = getenv("TERM");
  144.     // Unknown??  give up...
  145.     if (tt == 0) Error(sysErr,"Env. TERM not set!");
  146.     // remember type...
  147.      strcpy(TermType,tt);
  148.      // Fetch termcap entry...
  149.     if (tgetent(TCapBuf,TermType) <= 0)
  150.         Error(sysErr,"Unknown Terminal Type!");
  151.     // Fetch needed caps.
  152.      char* ptr = tcapbuf;
  153.     // pad character:
  154. #ifdef OSK
  155.     if (temp = tgetstr("pc",&ptr)) {
  156.         PC = PC_ = *temp;
  157.     } else PC = PC_ = 0;
  158. #else
  159.     if (temp = tgetstr("pc",&ptr)) {
  160.         PC = PC = *temp;
  161.     } else PC = PC = 0;
  162. #endif
  163.     ::BC = BC = tgetstr("bc",&ptr);        // backspace:
  164.     if (BC == 0) ::BC = BC = "\010";    // make sure it is something
  165.     ::UP = UP = tgetstr("up",&ptr);        // up line
  166.     CL = tgetstr("cl",&ptr);        // clear display
  167.     CM = tgetstr("cm",&ptr);        // cursor movement
  168.     CE = tgetstr("ce",&ptr);        // clear to EOL
  169.     CD = tgetstr("cd",&ptr);        // clear to EOS
  170.     SO = tgetstr("so",&ptr);        // standout begin
  171.     SE = tgetstr("se",&ptr);        // standout end
  172.      HO = tgetstr("ho",&ptr);        // home cursor
  173.     KD = tgetstr("kd",&ptr);        // down arrow key
  174.     KU = tgetstr("ku",&ptr);        // up arrow key
  175.     KR = tgetstr("kr",&ptr);        // right arrow key
  176.     KL = tgetstr("kl",&ptr);        // left arrow key
  177.     KH = tgetstr("kh",&ptr);        // home key
  178.     KB = tgetstr("kb",&ptr);        // backspace key
  179.     TI = tgetstr("ti",&ptr);        // term init string
  180.     TE = tgetstr("te",&ptr);        // term de-init string
  181.     lines = tgetnum("li");            // lines
  182.     colms = tgetnum("co");            // columns
  183. #ifdef OSK
  184.     ::ospeed = ospeed = ttym.sg_baud;    // baud rate
  185. #endif
  186. #ifdef unix
  187.     ::ospeed = ospeed = cfgetospeed(&ttym);
  188. #endif
  189.     // handle missing features
  190.     if (CE == 0) CE = "\007";
  191.     if (SO == 0) SO = "";
  192.     if (SE == 0) SE = "";
  193.     if (TI == 0) TI = "";
  194.     if (TE == 0) TE = "";
  195.     if (KD == 0) KD = "\016";    // ^N
  196.     if (KU == 0) KU = "\020";    // ^P
  197.     if (KR == 0) KR = "\006";    // ^F
  198.     if (KL == 0) KL = "\002";    // ^B
  199.     if (KH == 0) {
  200.         if (KD[0] == '\033' || KU[0] == '\033' ||
  201.             KR[0] == '\033' || KL[0] == '\033')
  202.             KH = "\033\033";    // ESC-ESC (^[^[)
  203.         else KH = "\033";    // ESC (^[)
  204.     }
  205.     if (KB == 0) KB = "\010";
  206.     // check to be sure terminal is not too dumb...
  207.     if (HO == 0) Error(sysErr,"Need ho");
  208.     if (CD == 0 && CL == 0) Error(sysErr,"Need either cd or cl!");
  209.     if (CM == 0) Error(sysErr,"Need cm");
  210.     if (lines < 1 || colms < 1) {
  211.             Error(sysErr,"Incomplete termcap entry");
  212.     }
  213.     if (CD == 0) CD = "\007";
  214.     // make sure we didn't overflow buffer
  215.     if (ptr >= &tcapbuf[TCapsLen]) {
  216.         Error(sysErr,"Terminal description too big!");
  217.     }
  218. #ifdef OSK
  219.     // setup for raw terminal modes
  220.     xttym = ttym;        // make a copy of the vanila setup
  221.     xttym.sg_backsp = 0;    // disable editing
  222.     xttym.sg_delete = 0;
  223.     xttym.sg_echo   = 0;    // echoing
  224.     xttym.sg_alf    = 0;    // auto lf's
  225.     xttym.sg_nulls  = 0;    // nulls
  226.     xttym.sg_pause  = 0;    // pausing
  227.     xttym.sg_bspch  = 0;    // backspacing
  228.     xttym.sg_dlnch  = 0;    // delete line
  229.     xttym.sg_eorch  = 0;    // end of record
  230.     xttym.sg_eofch  = 0;    // end of file
  231.     xttym.sg_rlnch  = 0;    // redraw line
  232.     xttym.sg_dulnch = 0;    
  233.     xttym.sg_psch   = 0;
  234.     xttym.sg_kbich  = 0;    // interrupting
  235.     xttym.sg_kbach  = 0;
  236.     xttym.sg_xon    = 0;    // xon/xoff (might be a bad idea...)
  237.     xttym.sg_xoff   = 0;
  238.     xttym.sg_tabcr  = 0;    // tab handling
  239.     // set raw mode
  240.     if (_ss_opt(0, &xttym) < 0) Error(sysErr,"for _ss_opt");
  241.     // setup interceptor
  242.     if (intercept((int (*)(...))Intercept) < 0) Error(sysErr,"for intercept");
  243. #endif    
  244. #ifdef unix
  245.     // setup for raw terminal modes
  246.     xttym = ttym;        // make a copy of the vanila setup
  247.     //xttym.c_iflag = ???;
  248.     //xttym.c_oflag = ???;
  249.     //xttym.c_cflag = ???;
  250.     xttym.c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ECHOCTL|ECHOPRT|ECHOKE|ICANON);
  251.     //xttym.c_line = ???;
  252.     for (int i = VERASE; i <= VEOL2; i++) xttym.c_cc[i] = 0;
  253.     if (tcsetattr(0, TCSANOW, &xttym) < 0) Error(sysErr,"for tcsetattr");
  254.     // setup interceptor
  255.     signal(SIGINT,(SignalHandler)Interrupt);
  256. #endif
  257. #endif
  258.     Clear();        // clear screen
  259.     InitChars();        // initialize terminal
  260. }
  261.  
  262. // Destructor:  restore terminal state
  263. Terminal::~Terminal ()
  264. {
  265.     PlainPen();        // plain pen
  266.     PenPos(lines-1,0);    // bottom of screen
  267.     ExitChars();        // de-init
  268. #ifdef OSK
  269.     // restore modes
  270.     if (_ss_opt(0, &ttym) < 0) Error(sysErr,"for _ss_opt");
  271.     // can interception of signals
  272.     if (intercept(0) < 0) Error(sysErr,"for intercept");
  273. #endif
  274. #ifdef unix
  275.     // restore modes
  276.         if (tcsetattr(0, TCSANOW, &(Terminal::ttym)) < 0) Error(sysErr,"for tcsetattr");
  277.         // can interception of signals
  278.         signal(SIGINT,SignalDefault);
  279. #endif
  280. }
  281. #ifdef MESSYDOS
  282. extern "C" {
  283.     void sound(int);
  284. }
  285. void Terminal::Bell()
  286. {
  287.     sound(440);
  288.     sleep(1);
  289.     sound(0);
  290. }
  291.  
  292. Terminal &Terminal::put(unsigned char ch)
  293. {
  294. #ifdef BARF1
  295.     gotoxy(ccol+1,cline+1);
  296. #endif
  297.     putch(ch);
  298. #ifdef BARF2
  299.     ccol++;
  300.     if (ccol >= colms)
  301.     {
  302.         ccol = 0;
  303.         cline++;
  304.         if (cline >= lines) cline = 0;
  305.     }
  306. #endif
  307. }
  308. // get a terminal key press. This function will hang and wait for a key press
  309. int Terminal::GetKey ()
  310. {
  311.     int ch;    // input key
  312.  
  313.     ch = kbd_getkey();
  314.     // check posible magic keys
  315.     switch (ch)
  316.     {
  317.         case K_ESC:
  318.         case K_HOME: return escCmd;
  319.         case K_CTLP:
  320.         case K_UP: return upCmd;
  321.         case K_CTLB:
  322.         case K_LEFT: return leftCmd;
  323.         case K_CTLF:
  324.         case K_RIGHT: return rightCmd;
  325.         case K_CTLN:
  326.         case K_DOWN: return downCmd; 
  327. //        case K_CTLH:
  328.         case K_BACK: return backCmd;
  329.         // plain key.
  330.         default: return ch;
  331.     }
  332. }
  333.  
  334. // check to see if a key has been pressed.  Does not hang.  Does not
  335. // actually read the key.
  336. Boolean Terminal::KeyAvailable()
  337. {
  338.     if ( kbd_ready() ) return true;
  339.     else return false;
  340. }
  341.  
  342. // Error handler
  343. void Terminal::Error (ErrKind err,const char *msg)
  344. {
  345.     if (errFun != 0) (*errFun)(err,msg);
  346.     else {
  347.         if (err == sysErr) {
  348.             // fetch system error message for system errors
  349.             printf("Error: %s %s\n",strerror(errno),msg);
  350.             Interrupt();
  351.         } else {
  352.             printf("Error: %s %s\n",
  353.                 (err == termErr ? "Terminal" : "Memory"),
  354.                 msg);
  355.             Interrupt();
  356.         }
  357.     }
  358. }
  359.  
  360. // Write a string to the message line (bottom terminal line) in standout
  361. // mode
  362. void Terminal::Message (const char* msg)
  363. {
  364.     int lastRow = lines;
  365.     int xcline = cline;
  366.     int xccol  = ccol;
  367.     RevsPen();
  368.     strncpy(termBuf,msg,colms);
  369.     register len = strlen(msg);
  370.     if (len >= colms) {
  371.         termBuf[len = (colms-1)] = '\0';
  372.         while (len >= (colms-1) - 3)  termBuf[--len] = '.';
  373.     } else {
  374.         while (len < (colms-1)) termBuf[len++] = ' ';
  375.         termBuf[len] = '\0';
  376.     }
  377.     PutStrAt(lastRow-1,0,termBuf);
  378.     PenPos(xcline,xccol); PlainPen();
  379. }
  380.  
  381. // read in a line of text. Editing is allowed and the input is echoed.
  382. int Terminal::GetLine (char* buffer,int bufsize,const char* terminators)
  383. {
  384.     int pos = 0;
  385.     int nchars = 0;
  386.     int ch;
  387.     // save base position
  388.     int xcline = cline;
  389.     int xccol  = ccol;
  390.     static char spaces[bufSize+2];
  391.  
  392.     memset(buffer,0,bufsize);    // clear out buffer
  393.     // determine max field width
  394.     int fieldwidth = (bufsize < (colms - xccol)
  395.              ? bufsize : (colms - xccol));
  396.     if (fieldwidth < 1) return(-1);    // field too small
  397.     // create a buffer of spaces (for clearing)
  398.     memset(spaces,' ',fieldwidth);
  399.     spaces[fieldwidth] = '\0';
  400.     // clear out field
  401.     PutStrAt(xcline,xccol,spaces);
  402.     PenPos(xcline,xccol);    // reset position
  403.     for (;;) {
  404.         ch = GetKey();    // get a key
  405.         switch (ch) {    // fan out based on key
  406.             case escCmd :     // Escape - flush buffer and start over
  407.                 memset(buffer,0,bufsize);
  408.                 PutStrAt(xcline,xccol,spaces);
  409.                 PenPos(xcline,xccol);
  410.                 nchars = 0;
  411.                 pos = 0;
  412.                 break;
  413.             case backCmd :    // backspace - delete character
  414.                 if (pos > 0) {
  415.                     strncpy(&buffer[pos-1],
  416.                         &buffer[pos],
  417.                         (nchars-pos)+1);
  418.                     PenPos(cline,ccol-1);
  419.                     put(&buffer[pos-1],
  420.                         (nchars-pos));
  421.                     put(' ');
  422.                     pos--; nchars--;
  423.                     PenPos(xcline,xccol+pos);
  424.                 } else Bell();
  425.                 break;
  426.             case deleCmd :    // delete line delete to BOF
  427.                 if (pos > 0) {
  428.                     strncpy(buffer,&buffer[pos],
  429.                         (nchars-pos)+1);
  430.                     PenPos(xcline,xccol);
  431.                     put(buffer,(nchars-pos));
  432.                     nchars -= pos;
  433.                     pos = 0;
  434.                     put(spaces,
  435.                         fieldwidth-nchars);
  436.                     PenPos(xcline,xccol+pos);
  437.                 } else Bell();
  438.                 break;
  439.             case rightCmd :    // right arrow - move right
  440.                 if (pos < nchars) {
  441.                     pos++;
  442.                     PenPos(xcline,xccol+pos);
  443.                 } else Bell();
  444.                 break;
  445.             case leftCmd :    // left arror - move left
  446.                 if (pos > 0) {
  447.                     PenPos(cline,ccol-1);
  448.                     pos--;
  449.                 } else Bell();
  450.                 break;
  451.             default :    // something else.
  452.                 // terminator??
  453.                 if (strchr(terminators,ch) != 0) {
  454.                     // yep.  reset pos and return
  455.                     PenPos(xcline,xccol);
  456.                     return(nchars);
  457.                 } else if (pos == nchars && // no room?
  458.                        nchars >= fieldwidth) Bell();
  459.                 else {
  460.                     if (ch < ' ') Bell();    // funny char?
  461.                     else {
  462.                         // normal charactor.  shove
  463.                         // into buffer and echo.
  464.                         buffer[pos] = ch;
  465.                         put(buffer[pos]);
  466.                         pos++;
  467.                         if (pos > nchars) nchars++;
  468.                     }
  469.                 }
  470.                 break;
  471.         }
  472.     }
  473. }
  474.  
  475. // put a character at some specified place
  476. void Terminal::PutCharAt (int row,int col,int ch)
  477. {
  478.     if (col >= colms) {
  479.         row++;
  480.         col = 0;
  481.     }
  482.     PenPos(row,col);
  483.     char cch = ch & charMask;
  484.     Mode m = (Mode) (ch & modeMask);
  485.     if (m == revsPen) RevsPen();
  486.     else if (m == defPen) PlainPen();
  487.     put(cch);
  488.     ccol++;
  489. }
  490.  
  491. // Put a plain string someplace
  492. void Terminal::PutStrAt (int row,int col,const char* str)
  493. {
  494.     int slen = strlen(str);
  495.     int mright = colms - (col+slen);
  496.     int right  = (mright < 0 ? -mright : 0);
  497.     int left   = slen - right;
  498.     if (left > 0) {
  499.         PenPos(row,col);
  500.         put((char*)str,left);
  501.         ccol += left;
  502.     }
  503.     if (right > 0) {
  504.         PenPos(row+1,0);
  505.         put((char*)(str+left),right);
  506.         ccol += right;
  507.     }
  508. }
  509.  
  510. // like above,  but this is a string of chars with attributes
  511. void Terminal::PutStrAt (int row,int col,const short* str)
  512. {
  513.     while (*str != 0) {
  514.         PutCharAt(row,col,(int)*str);
  515.         row = cline; col = ccol;
  516.         str++;
  517.     }
  518. }
  519.  
  520. //Put a plain string in a rectangluar area.  If the string is too long,
  521. //returns a pointer to the un-displayed tail of the string.  Returns
  522. //NULL (0) if the string fit.  Does dump wrapping (indicated with a '\'
  523. //and displayes non-printable characters with a '.'.
  524. char* Terminal::PutStrInBox(int row,int col,int width,int height,const char* str)
  525. {
  526.     int rrow = 0,rcol = 0;
  527.     PenPos(row,col);
  528.     for (char* p = (char*)str; *p != '\0'; p++) {
  529.         if (rcol == width) {
  530.             put('\\');
  531.             rcol = 0;
  532.             rrow++;
  533.             PenPos(row+rrow,col);
  534.         }
  535.         if (rrow >= height) return(p);
  536.         else if (*p == '\n') {
  537.             while (rcol++ < width) put(' ');
  538.             rcol = 0;
  539.             rrow++;
  540.             PenPos(row+rrow,col);
  541.         } else if (*p < ' ') {
  542.             put('.');
  543.             rcol++;
  544.             ccol++;
  545.         } else {
  546.             put(*p);
  547.             rcol++;
  548.             ccol++;
  549.         }
  550.     }
  551.     while (rrow < height) {
  552.         while (rcol < width) {
  553.             put(' ');
  554.             rcol++;
  555.             ccol++;
  556.         }
  557.         rcol = 0;
  558.         rrow++;
  559.         if (rrow < height) PenPos(row+rrow,col);
  560.     }
  561.     return(0);
  562. }
  563. #else
  564. // code output functions.
  565. static int tputc (char c) {return write(1,&c,1);}
  566. void Terminal::WriteCode(const char* code) {tputs(code,1,tputc);}
  567. void Terminal::WriteCodeLines(const char* code,int l) {tputs(code,l,tputc);}
  568.  
  569. // set cursor position
  570. void Terminal::PenPos (int row, int col)
  571. {
  572.     row = (row < 0 ? 0 : (row >= lines ? lines - 1 : row));
  573.     col = (col < 0 ? 0 : (col >= colms ? colms - 1 : col));
  574.     PosCode(termBuf,row+1,col+1);
  575.     WriteCode(termBuf);
  576.     cline = row; ccol = col;
  577. }
  578.  
  579. // get a terminal key press. This function will hang and wait for a key press
  580. int Terminal::GetKey ()
  581. {
  582.     char ch;    // input key
  583.     char buff[10];    // look ahead buffer
  584.  
  585.     for (int k = 0;;) {
  586.         // get a character
  587. #ifdef unix
  588.         while (!KeyAvailable()) ;
  589. #endif
  590.         read(0,&ch,1);
  591.         buff[k++] = ch;        // save in buffer
  592.         // check posible magic keys
  593.         if (strncmp(buff,KD,k) == 0) {
  594.             if (KD[k] == 0) return downCmd;
  595.         } else if (strncmp(buff,KU,k) == 0) {
  596.             if (KU[k] == 0) return upCmd;
  597.         } else if (strncmp(buff,KR,k) == 0) {
  598.             if (KR[k] == 0) return rightCmd;
  599.         } else if (strncmp(buff,KL,k) == 0) {
  600.             if (KL[k] == 0) return leftCmd;
  601.         } else if (strncmp(buff,KH,k) == 0) {
  602.             if (KH[k] == 0) return escCmd;
  603.         } else if (strncmp(buff,KB,k) == 0) {
  604.             if (KB[k] == 0) return backCmd;
  605.         } else if (k > 1) { // no known prefix.  must be some other key
  606.             // sort of prefix - probably undefined function key
  607.             Bell();
  608.             k = 0;
  609.             continue;
  610.         // magic single character keys
  611.         } else if (ch == '\016') return downCmd;    // ^N
  612.         else if (ch == '\020') return upCmd;        // ^P
  613.         else if (ch == '\006') return rightCmd;        // ^F
  614.         else if (ch == '\002') return leftCmd;        // ^B
  615.         else if (ch == '\010') return backCmd;        // ^H
  616.         else if (ch == '\177') return backCmd;        // RUBOUT
  617.         else if (ch == '\025') return deleCmd;        // ^U
  618.         // plain key.
  619.         else return (int) ch;
  620.     }
  621. }
  622.  
  623. // check to see if a key has been pressed.  Does not hang.  Does not
  624. // actually read the key.
  625. Boolean Terminal::KeyAvailable()
  626. {
  627. #ifdef OSK
  628.     return(_gs_rdy(0) > 0);
  629. #endif
  630. #ifdef unix
  631.     int bytes_avail;
  632.     ioctl(0,TIOCINQ,&bytes_avail);
  633.     return (bytes_avail > 0);
  634. #endif
  635. }
  636.  
  637. // Error handler
  638. void Terminal::Error (ErrKind err,const char *msg)
  639. {
  640.     if (errFun != 0) (*errFun)(err,msg);
  641.     else {
  642.         if (err == sysErr) {
  643.             // fetch system error message for system errors
  644.             sprintf(termBuf,"Error: %s %s\n",strerror(errno),msg);
  645.             write(1,termBuf,strlen(termBuf));
  646.             Interrupt();
  647.         } else {
  648.             sprintf(termBuf,"Error: %s %s\n",
  649.                 (err == termErr ? "Terminal" : "Memory"),
  650.                 msg);
  651.             write(1,termBuf,strlen(termBuf));
  652.             Interrupt();
  653.         }
  654.     }
  655. }
  656.  
  657. // Write a string to the message line (bottom terminal line) in standout
  658. // mode
  659. void Terminal::Message (const char* msg)
  660. {
  661.     int lastRow = lines;
  662.     int xcline = cline;
  663.     int xccol  = ccol;
  664.     PenPos(lastRow,0); RevsPen();
  665.     strncpy(termBuf,msg,colms);
  666.     register len = strlen(msg);
  667.     if (len >= colms) {
  668.         termBuf[len = (colms-1)] = '\0';
  669.         while (len >= (colms-1) - 3)  termBuf[--len] = '.';
  670.     } else {
  671.         while (len < (colms-1)) termBuf[len++] = ' ';
  672.         termBuf[len] = '\0';
  673.     }
  674.     write(1,termBuf,len);
  675.     PenPos(xcline,xccol); PlainPen();
  676. }
  677.  
  678. // read in a line of text. Editing is allowed and the input is echoed.
  679. int Terminal::GetLine (char* buffer,int bufsize,const char* terminators)
  680. {
  681.     int pos = 0;
  682.     int nchars = 0;
  683.     int ch;
  684.     // save base position
  685.     int xcline = cline;
  686.     int xccol  = ccol;
  687.     static char spaces[bufSize];
  688.  
  689.     memset(buffer,0,bufsize);    // clear out buffer
  690.     // determine max field width
  691.     int fieldwidth = (bufsize < (colms - xccol)
  692.              ? bufsize : (colms - xccol));
  693.     if (fieldwidth < 1) return(-1);    // field too small
  694.     // create a buffer of spaces (for clearing)
  695.     memset(spaces,' ',fieldwidth);
  696.     // clear out field
  697.     write(1,spaces,fieldwidth);
  698.     PenPos(xcline,xccol);    // reset position
  699.     for (;;) {
  700.         ch = GetKey();    // get a key
  701.         switch (ch) {    // fan out based on key
  702.             case escCmd :     // Escape - flush buffer and start over
  703.                 memset(buffer,0,bufsize);
  704.                 PenPos(xcline,xccol);
  705.                 write(1,spaces,fieldwidth);
  706.                 PenPos(xcline,xccol);
  707.                 nchars = 0;
  708.                 pos = 0;
  709.                 break;
  710.             case backCmd :    // backspace - delete character
  711.                 if (pos > 0) {
  712.                     strncpy(&buffer[pos-1],
  713.                         &buffer[pos],
  714.                         (nchars-pos)+1);
  715.                     WriteCode(BC);
  716.                     write(1,&buffer[pos-1],
  717.                         (nchars-pos));
  718.                     write(1,spaces,1);
  719.                     pos--; nchars--;
  720.                     PenPos(xcline,xccol+pos);
  721.                 } else Bell();
  722.                 break;
  723.             case deleCmd :    // delete line delete to BOF
  724.                 if (pos > 0) {
  725.                     strncpy(buffer,&buffer[pos],
  726.                         (nchars-pos)+1);
  727.                     PenPos(xcline,xccol);
  728.                     write(1,buffer,(nchars-pos));
  729.                     nchars -= pos;
  730.                     pos = 0;
  731.                     write(1,spaces,
  732.                         fieldwidth-nchars);
  733.                     PenPos(xcline,xccol+pos);
  734.                 } else Bell();
  735.                 break;
  736.             case rightCmd :    // right arrow - move right
  737.                 if (pos < nchars) {
  738.                     pos++;
  739.                     PenPos(xcline,xccol+pos);
  740.                 } else Bell();
  741.                 break;
  742.             case leftCmd :    // left arror - move left
  743.                 if (pos > 0) {
  744.                     WriteCode(BC);
  745.                     pos--;
  746.                 } else Bell();
  747.                 break;
  748.             default :    // something else.
  749.                 // terminator??
  750.                 if (strchr(terminators,ch) != 0) {
  751.                     // yep.  reset pos and return
  752.                     PenPos(xcline,xccol);
  753.                     return(nchars);
  754.                 } else if (pos == nchars && // no room?
  755.                        nchars >= fieldwidth) Bell();
  756.                 else {
  757.                     if (ch < ' ') Bell();    // funny char?
  758.                     else {
  759.                         // normal charactor.  shove
  760.                         // into buffer and echo.
  761.                         buffer[pos] = ch;
  762.                         write(1,&buffer[pos],1);
  763.                         pos++;
  764.                         if (pos > nchars) nchars++;
  765.                     }
  766.                 }
  767.                 break;
  768.         }
  769.     }
  770. }
  771.  
  772. // put a character at some specified place
  773. void Terminal::PutCharAt (int row,int col,int ch)
  774. {
  775.     if (col >= colms) {
  776.         row++;
  777.         col = 0;
  778.     }
  779.     PenPos(row,col);
  780.     char cch = ch & charMask;
  781.     Mode m = (Mode) (ch & modeMask);
  782.     if (m == revsPen) RevsPen();
  783.     else if (m == defPen) PlainPen();
  784.     write(1,&cch,1);
  785.     ccol++;
  786. }
  787.  
  788. // Put a plain string someplace
  789. void Terminal::PutStrAt (int row,int col,const char* str)
  790. {
  791.     int slen = strlen(str);
  792.     int mright = colms - (col+slen);
  793.     int right  = (mright < 0 ? -mright : 0);
  794.     int left   = slen - right;
  795.     if (left > 0) {
  796.         PenPos(row,col);
  797.         write(1,(char*)str,left);
  798.         ccol += left;
  799.     }
  800.     if (right > 0) {
  801.         PenPos(row+1,0);
  802.         write(1,(char*)(str+left),right);
  803.         ccol += right;
  804.     }
  805. }
  806.  
  807. // like above,  but this is a string of chars with attributes
  808. void Terminal::PutStrAt (int row,int col,const short* str)
  809. {
  810.     while (*str != 0) {
  811.         PutCharAt(row,col,(int)*str);
  812.         row = cline; col = ccol;
  813.         str++;
  814.     }
  815. }
  816.  
  817. //Put a plain string in a rectangluar area.  If the string is too long,
  818. //returns a pointer to the un-displayed tail of the string.  Returns
  819. //NULL (0) if the string fit.  Does dump wrapping (indicated with a '\'
  820. //and displayes non-printable characters with a '.'.
  821. char* Terminal::PutStrInBox(int row,int col,int width,int height,const char* str)
  822. {
  823.     int rrow = 0,rcol = 0;
  824.     PenPos(row,col);
  825.     for (char* p = (char*)str; *p != '\0'; p++) {
  826.         if (rcol == width) {
  827.             write(1,"\\",1);
  828.             rcol = 0;
  829.             rrow++;
  830.             PenPos(row+rrow,col);
  831.         }
  832.         if (rrow >= height) return(p);
  833.         else if (*p == '\n') {
  834.             while (rcol++ < width) write(1," ",1);
  835.             rcol = 0;
  836.             rrow++;
  837.             PenPos(row+rrow,col);
  838.         } else if (*p < ' ') {
  839.             write(1,".",1);
  840.             rcol++;
  841.             ccol++;
  842.         } else {
  843.             write(1,p,1);
  844.             rcol++;
  845.             ccol++;
  846.         }
  847.     }
  848.     while (rrow < height) {
  849.         while (rcol < width) {
  850.             write(1," ",1);
  851.             rcol++;
  852.             ccol++;
  853.         }
  854.         rcol = 0;
  855.         rrow++;
  856.         if (rrow < height) PenPos(row+rrow,col);
  857.     }
  858.     return(0);
  859. }
  860. #endif    
  861.             
  862.             
  863.  
  864. // process forking - very O/S dependent and posible hairy...
  865. #ifdef OSK
  866.  
  867. extern char** environ;
  868.  
  869. typedef int (*ProcFunc)(const char* modname,int parmsize,const char *parmptr,
  870.              short type,short lang,int datasize,short prior,
  871.              short pathcnt);
  872.  
  873.  
  874. extern "MWC" {
  875.     int os9exec(ProcFunc procfunc,const char* modname,const char** argv,
  876.             const char** envp,unsigned stacksize,short priority,
  877.             short pathcnt);
  878. };
  879.  
  880.  
  881.  
  882.  
  883.  
  884. // fork a process
  885. int Terminal::forkprog (const char** argv)
  886. {
  887.     static char errorbuffer[512];
  888.     // remember where we are
  889.     int xcline = cline;
  890.     int xccol  = ccol;
  891.     // reset position and reset terminal
  892.     PenPos(lines-1,0);
  893.     PlainPen();
  894.     ExitChars();
  895.     _ss_opt(0, &ttym);
  896.     // try to fork the process
  897.     int pid = os9exec(os9forkc,argv[0],argv,environ,0,0,3);
  898.     // failure??
  899.     if (pid < 0) {
  900.         // yep.  revert terminal and spit out a message
  901.         int error = errno;
  902.         _ss_opt(0, &xttym);
  903.         InitChars();
  904.         cline = xcline; ccol = xccol;
  905.         sprintf(errorbuffer,"Error in fork: %s",strerror(error));
  906.         Message(errorbuffer);
  907.         errno = error;
  908.         return (-1);    // display not disturbed
  909.     } else {
  910.         // fork worked.  Wait for process to finish
  911.         int error = 0;
  912.         while (wait(&error) == 0) ;    // waiting...
  913.         error &= 0x00FFFF;        // completion status...
  914.         // revert terminal
  915.         _ss_opt(0, &xttym);
  916.         InitChars();
  917.         cline = xcline; ccol = xccol;
  918.         // did process crash??
  919.         if (error == 0) {
  920.             // nope
  921.             PenPos(xcline,xccol);
  922.             // success, but display might be trashed
  923.             return(1);
  924.         } else {
  925.             // child crashed.
  926.             // fetch error message
  927.             sprintf(errorbuffer,"Error in child: %s",
  928.                 strerror(error));
  929.             Message(errorbuffer);
  930.             // stash error code
  931.             errno = error;
  932.             // process crashed, but might have trashed the display
  933.             return (0);
  934.         }
  935.     }
  936. }
  937. #endif
  938. #ifdef MESSYDOS
  939. #include <process.h>
  940. #include <string.h>
  941. // fork a process
  942. int Terminal::forkprog (const char** argv)
  943. {
  944.     static char errorbuffer[512];
  945.     // remember where we are
  946.     int xcline = cline;
  947.     int xccol  = ccol;
  948.     // reset position and reset terminal
  949.     PenPos(lines-1,0);
  950.     PlainPen();
  951.     ExitChars();
  952.     // try to fork the process
  953.     int error = spawnvp(P_WAIT,argv[0],argv);
  954.     // failure??
  955.     error &= 0x00FFFF;        // completion status...
  956.     // revert terminal
  957.     InitChars();
  958.     cline = xcline; ccol = xccol;
  959.     // did process crash??
  960.     if (error == 0) {
  961.         // nope
  962.         PenPos(xcline,xccol);
  963.         // success, but display might be trashed
  964.         return(1);
  965.     } else if (error < 0) {
  966.         // child was stillborn
  967.         error = errno;
  968.         InitChars();
  969.         cline = xcline; ccol = xccol;
  970.         sprintf(errorbuffer,"Error in fork: %s",strerror(error));
  971.         Message(errorbuffer);
  972.         errno = error;
  973.         return (-1);    // display not disturbed
  974.     } else {
  975.         // child crashed.
  976.         // fetch error message
  977.         sprintf(errorbuffer,"Error in child: %s",
  978.             strerror(error));
  979.         Message(errorbuffer);
  980.         // stash error code
  981.         errno = error;
  982.         // process crashed, but might have trashed the display
  983.         return (0);
  984.     }
  985. }
  986.  
  987. #endif
  988. #ifdef unix
  989. #include <unistd.h>
  990. #include <sys/types.h>
  991. #include <sys/wait.h>
  992. #include <string.h>
  993. // fork a process
  994. int Terminal::forkprog (const char** argv)
  995. {
  996.     pid_t child;
  997.     int error;
  998.     static char errorbuffer[512];
  999.     // remember where we are
  1000.     int xcline = cline;
  1001.     int xccol  = ccol;
  1002.     // reset position and reset terminal
  1003.     PenPos(lines-1,0);
  1004.     PlainPen();
  1005.     ExitChars();
  1006.     // try to fork the process
  1007.     child = fork();
  1008.     if (child < 0)
  1009.     {
  1010.         error = child;
  1011.     } else if (child > 0)
  1012.     {
  1013.         waitpid(child,&error,0);
  1014.     } else
  1015.     {
  1016.         _exit(execvp(argv[0],argv));
  1017.     }
  1018.     // failure??
  1019.     error &= 0x00FFFF;        // completion status...
  1020.     // revert terminal
  1021.     InitChars();
  1022.     cline = xcline; ccol = xccol;
  1023.     // did process crash??
  1024.     if (error == 0) {
  1025.         // nope
  1026.         PenPos(xcline,xccol);
  1027.         // success, but display might be trashed
  1028.         return(1);
  1029.     } else if (error < 0) {
  1030.         // child was stillborn
  1031.         error = errno;
  1032.         InitChars();
  1033.         cline = xcline; ccol = xccol;
  1034.         sprintf(errorbuffer,"Error in fork: %s",strerror(error));
  1035.         Message(errorbuffer);
  1036.         errno = error;
  1037.         return (-1);    // display not disturbed
  1038.     } else {
  1039.         // child crashed.
  1040.         // fetch error message
  1041.         sprintf(errorbuffer,"Error in child: %s",
  1042.             strerror(error));
  1043.         Message(errorbuffer);
  1044.         // stash error code
  1045.         errno = error;
  1046.         // process crashed, but might have trashed the display
  1047.         return (0);
  1048.     }
  1049. }
  1050.  
  1051. #endif
  1052.  
  1053.